/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
/**
 * 
 */
package com.genia.toolbox.persistence.hibernate.manager;

import com.genia.toolbox.basics.bean.impl.ConstantDelimiter;
import com.genia.toolbox.persistence.basis.manager.AbstractManager;
import com.genia.toolbox.persistence.criteria.CriteriaType;
import com.genia.toolbox.persistence.criteria.PropertyField;
import com.genia.toolbox.persistence.criteria.field.Field;
import com.genia.toolbox.persistence.exception.AggregateException;
import com.genia.toolbox.persistence.hibernate.criteria.HibernateCriteriaType;
import com.genia.toolbox.persistence.hibernate.criteria.field.DefaultHibernateField;
import com.genia.toolbox.persistence.hibernate.criteria.field.HibernateConstantField;
import com.genia.toolbox.persistence.hibernate.criteria.field.HibernateField;
import com.genia.toolbox.persistence.hibernate.criteria.field.HibernatePropertyField;
import com.genia.toolbox.persistence.manager.FieldManager;

/**
 * Hibernate implementation of the <code>FieldManager</code> that allows to
 * define complex operation on columns.
 */
public class HibernateFieldManager
    extends AbstractManager
    implements FieldManager
{

  /**
   * Return a field that represents the addition of 2 fields.
   * 
   * @param field1
   *          the first field to be added
   * @param field2
   *          the second field to be added
   * @return a field representing <code>field1 + field2</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public HibernateField addField(final Field field1, final Field field2)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("(", ")+(", ")"), (HibernateField) field1, (HibernateField) field2);
  }



  /**
   * This method return a <code>Field</code> that represents the size of
   * another field that must be a collection.
   * 
   * @param field
   *          the field to inspect
   * @return the field representing the size of the {@link Field} given in
   *         parameter
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   * @see com.genia.toolbox.persistence.manager.FieldManager#size(com.genia.toolbox.persistence.criteria.field.Field)
   */
  public Field size(Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("size(", "", ")"), (HibernateField) field);
  }



  /**
   * Return a field that represents the concatenation of 2 fields.
   * 
   * @param field1
   *          the first field to be concatened
   * @param field2
   *          the second field to be concatened
   * @return a field representing the concatenation of <code>field1</code> and
   *         <code>field2</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public HibernateField concatField(final Field field1, final Field field2)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("(", ")||(", ")"), (HibernateField) field1, (HibernateField) field2);
  }



  /**
   * Return a field that represents the division of 2 fields.
   * 
   * @param field1
   *          the first field to be divided
   * @param field2
   *          the second field to be divided
   * @return a field representing <code>field1 / field2</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public HibernateField divField(final Field field1, final Field field2)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("(", ")/(", ")"), (HibernateField) field1, (HibernateField) field2);
  }



  /**
   * Return a field that represents a constant.
   * 
   * @param value
   *          The value of this field
   * @return The field representing the value
   */
  public HibernateField getConstantField(final Object value)
  {
    return new HibernateConstantField(value);
  }



  /**
   * This method return a <code>Field</code> that represents the object
   * associated to the type passed in argument.
   * 
   * @param type
   *          the <code>CriteriaType</code> the resulting field must represent
   * @return the field representing the object of the type passed in argument
   */
  public Field getObjectField(final CriteriaType<?> type)
  {
    return new HibernatePropertyField(((HibernateCriteriaType<?>) type));
  }



  /**
   * This method return a <code>Field</code> that represents a property of the
   * type passed in argument of a criteria.
   * 
   * @param type
   *          the type this field refers to
   * @param property
   *          the name of the property to represent
   * @return the field representing the property passed in argument
   */
  public HibernateField getPropertyField(final CriteriaType<?> type, final PropertyField<?, ?> property)
  {
    return new HibernatePropertyField(((HibernateCriteriaType<?>) type), property.getPropertyName());
  }



  /**
   * This method return a <code>Field</code> that represents a property of the
   * main type of a criteria.
   * 
   * @param property
   *          the name of the property to represent
   * @return the field representing the property passed in argument
   */
  public HibernateField getPropertyField(final PropertyField<?, ?> property)
  {
    return new HibernatePropertyField(property.getPropertyName());
  }



  /**
   * This method return a {@link Field} that represents the elements of another
   * {@link Field} of type <code>Collection</code>. This Field can then be
   * used in isIn restrictions and such.
   * 
   * @param field
   *          the field to get the elements from
   * @return the field representing the elements of another {@link Field}
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   * @see com.genia.toolbox.persistence.manager.FieldManager#elements(com.genia.toolbox.persistence.criteria.field.Field)
   */
  public Field elements(Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("elements(", "", ")"), (HibernateField) field);
  }



  /**
   * Return a field that represents the lower case representation of a field.
   * 
   * @param field
   *          the field to be converted
   * @return a field representing the lower case version of <code>field</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public HibernateField lowerCase(final Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("lower(", "", ")"), (HibernateField) field);
  }



  /**
   * Return a field that represents the multiplication of 2 fields.
   * 
   * @param field1
   *          the first field to be multiplicated
   * @param field2
   *          the second field to be multiplicated
   * @return a field representing <code>field1 * field2</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public HibernateField multField(final Field field1, final Field field2)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("(", ")*(", ")"), (HibernateField) field1, (HibernateField) field2);
  }



  /**
   * Return a field that represents the substraction of 2 fields.
   * 
   * @param field1
   *          the first field to be subtsracted
   * @param field2
   *          the second field to be subtsracted
   * @return a field representing <code>field1 - field2</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public HibernateField substractField(final Field field1, final Field field2)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("(", ")-(", ")"), (HibernateField) field1, (HibernateField) field2);
  }



  /**
   * Return a field that represents the upper case representation of a field.
   * 
   * @param field
   *          the field to be converted
   * @return a field representing the upper case version of <code>field</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public HibernateField upperCase(final Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(false, new ConstantDelimiter("upper(", "", ")"), (HibernateField) field);
  }



  /**
   * Return a field that compute the average value of a numeric field in an
   * aggregate.
   * 
   * @param field
   *          the field to compute the average on
   * @return a field that compute the average value of a numeric field in an
   *         aggregate.
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public Field avg(final Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(true, new ConstantDelimiter("avg(", "", ")"), (HibernateField) field);
  }



  /**
   * Return a field that count the number of aggregate line.
   * 
   * @return a field that count the number of aggregate line
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public Field count()
      throws AggregateException
  {
    return new DefaultHibernateField(true, new ConstantDelimiter("count(*)", "", ""));
  }



  /**
   * Return a field that compute the maximal value of a numeric field in an
   * aggregate.
   * 
   * @param field
   *          the field to compute the maximal on
   * @return a field that compute the maximal value of a numeric field in an
   *         aggregate.
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public Field max(final Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(true, new ConstantDelimiter("max(", "", ")"), (HibernateField) field);
  }



  /**
   * Return a field that compute the minimal value of a numeric field in an
   * aggregate.
   * 
   * @param field
   *          the field to compute the minimal on
   * @return a field that compute the minimal value of a numeric field in an
   *         aggregate.
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public Field min(final Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(true, new ConstantDelimiter("min(", "", ")"), (HibernateField) field);
  }



  /**
   * Return a field that compute the sum of a numeric field in an aggregate.
   * 
   * @param field
   *          the field to compute the sum on
   * @return a field that compute the sum of a numeric field in an aggregate.
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public Field sum(final Field field)
      throws AggregateException
  {
    return new DefaultHibernateField(true, new ConstantDelimiter("sum(", "", ")"), (HibernateField) field);
  }

}
